﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Net;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using System.Collections;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace WxPayAPI
{
    public class WxPayApi
    {
        /**
        *    
        * 查询订单
        * @param WxPayData inputObj 提交给查询订单API的参数
        * @param int timeOut 超时时间
        * @throws WxPayException
        * @return 成功时返回订单查询结果，其他抛异常
        */
        public static WxPayData OrderQuery(WxPayData inputObj, int timeOut = 6)
        {
            //检测必填参数
            if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
            {
                throw new WxPayException("订单查询接口中，out_trade_no、transaction_id必填！");
            }
            string out_trade_no = inputObj.GetValue("out_trade_no").ToString();
            //string transaction_id = inputObj.GetValue("transaction_id").ToString();
            string mchid = WxPayConfig.MCHID;

            string url = $"https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}?mchid={mchid}";

            var start = DateTime.Now;

            Log.Debug("WxPayApi", "OrderQuery request : " + url);

            //生成签名
            string nonceStr = GetRandomDom(32);//随机字符串
            Log.Debug("WxPayApi", "UnfiedOrder request 随机字符串: " + nonceStr);
            long timeStamp = ConvertDateTimeToInt(DateTime.Now); //时间戳
            Log.Debug("WxPayApi", "UnfiedOrder request 时间戳: " + timeStamp);
            string method = "GET"; //HTTP请求方式
            string uri = $"/v3/pay/transactions/out-trade-no/{out_trade_no}?mchid={mchid}"; //API接口请求参数的json字符串
            string content=""; //报文主体
            string sign = $"{method}\n{uri}\n{timeStamp}\n{nonceStr}\n{content}\n";
            Log.Debug("WxPayApi", "UnfiedOrder request 签名前: " + sign);
            var paySign = RSAEncrypt(sign);
            Log.Debug("WxPayApi", "UnfiedOrder request 签名后: " + paySign);

            //发起请求
            string response = HttpService.Get(url, paySign, nonceStr, timeStamp);//调用HTTP通信接口
            Log.Debug("WxPayApi", "OrderQuery response : " + response);

            var end = DateTime.Now;
            int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时

            //将xml格式的数据转化为对象以返回
            WxPayData result = new WxPayData();
            Hashtable hashtable = result.FromJson(response);
            result.SetValue("trade_state", hashtable["trade_state"]);

            ReportCostTime(url, timeCost, result);//测速上报

            return result;
        }


        /**
        * 
        * JSAPI下单
        * @param WxPaydata inputObj 提交给统一下单API的参数
        * @param int timeOut 超时时间
        * @throws WxPayException
        * @return 成功时返回，其他抛异常
        */
        public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 6)
        {
            string url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
            //检测必填参数
            if (!inputObj.IsSet("out_trade_no"))
            {
                throw new WxPayException("缺少统一支付接口必填参数out_trade_no！");
            }
            else if (!inputObj.IsSet("description"))
            {
                throw new WxPayException("缺少统一支付接口必填参数description！");
            }
            else if (!inputObj.IsSet("total"))
            {
                throw new WxPayException("缺少统一支付接口必填参数total！");
            }
            else if (!inputObj.IsSet("openid"))
            {
                throw new WxPayException("缺少统一支付接口必填参数openid！");
            }

            //异步通知url未设置，则使用配置文件中的url
            if (!inputObj.IsSet("notify_url"))
            {
                inputObj.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//异步通知url
            }

            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
            inputObj.SetValue("mchid", WxPayConfig.MCHID);//商户号
            string xjson = inputObj.ToWechatJspaiJson(); //报文主体

            var start = DateTime.Now;

            Log.Debug("WxPayApi", "UnfiedOrder request : " + xjson);

            //生成签名
            string nonceStr = GetRandomDom(32);//随机字符串
            Log.Debug("WxPayApi", "UnfiedOrder request 随机字符串: " + nonceStr);
            long timeStamp = ConvertDateTimeToInt(DateTime.Now); //时间戳
            Log.Debug("WxPayApi", "UnfiedOrder request 时间戳: " + timeStamp);
            string method = "POST"; //HTTP请求方式
            string uri = "/v3/pay/transactions/jsapi"; //API接口请求参数的json字符串
            string sign = $"{method}\n{uri}\n{timeStamp}\n{nonceStr}\n{xjson}\n";
            Log.Debug("WxPayApi", "UnfiedOrder request 签名前: " + sign);
            var paySign = RSAEncrypt(sign);
            Log.Debug("WxPayApi", "UnfiedOrder request 签名后: " + paySign);

            //发起请求
            string response = HttpService.Post(xjson, url, false, timeOut, paySign, nonceStr, timeStamp);
            Log.Debug("WxPayApi", "UnfiedOrder response : " + response);

            var end = DateTime.Now;
            int timeCost = (int)((end - start).TotalMilliseconds);

            WxPayData result = new WxPayData();
            //result.FromXml(response);
            Hashtable hashtable = result.FromJson(response);
            result.SetValue("prepay_id", hashtable["prepay_id"]);

            ReportCostTime(url, timeCost, result);//测速上报

            return result;
        }


        /**
	    * 
	    * 测速上报
	    * @param string interface_url 接口URL
	    * @param int timeCost 接口耗时
	    * @param WxPayData inputObj参数数组
	    */
        private static void ReportCostTime(string interface_url, int timeCost, WxPayData inputObj)
        {
            //如果不需要进行上报
            if (WxPayConfig.REPORT_LEVENL == 0)
            {
                return;
            }

            //如果仅失败上报
            if (WxPayConfig.REPORT_LEVENL == 1 && inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" &&
             inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS")
            {
                return;
            }

            //上报逻辑
            WxPayData data = new WxPayData();
            data.SetValue("interface_url", interface_url);
            data.SetValue("execute_time_", timeCost);
            //返回状态码
            if (inputObj.IsSet("return_code"))
            {
                data.SetValue("return_code", inputObj.GetValue("return_code"));
            }
            //返回信息
            if (inputObj.IsSet("return_msg"))
            {
                data.SetValue("return_msg", inputObj.GetValue("return_msg"));
            }
            //业务结果
            if (inputObj.IsSet("result_code"))
            {
                data.SetValue("result_code", inputObj.GetValue("result_code"));
            }
            //错误代码
            if (inputObj.IsSet("err_code"))
            {
                data.SetValue("err_code", inputObj.GetValue("err_code"));
            }
            //错误代码描述
            if (inputObj.IsSet("err_code_des"))
            {
                data.SetValue("err_code_des", inputObj.GetValue("err_code_des"));
            }
            //商户订单号
            if (inputObj.IsSet("out_trade_no"))
            {
                data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no"));
            }
            //设备号
            if (inputObj.IsSet("device_info"))
            {
                data.SetValue("device_info", inputObj.GetValue("device_info"));
            }

            try
            {
                Report(data);
            }
            catch (WxPayException ex)
            {
                //不做任何处理
            }
        }


        /**
	    * 
	    * 测速上报接口实现
	    * @param WxPayData inputObj 提交给测速上报接口的参数
	    * @param int timeOut 测速上报接口超时时间
	    * @throws WxPayException
	    * @return 成功时返回测速上报接口返回的结果，其他抛异常
	    */
        public static WxPayData Report(WxPayData inputObj, int timeOut = 1)
        {
            string url = "https://api.mch.weixin.qq.com/payitil/report";
            //检测必填参数
            if (!inputObj.IsSet("interface_url"))
            {
                throw new WxPayException("接口URL，缺少必填参数interface_url！");
            }
            if (!inputObj.IsSet("return_code"))
            {
                throw new WxPayException("返回状态码，缺少必填参数return_code！");
            }
            if (!inputObj.IsSet("result_code"))
            {
                throw new WxPayException("业务结果，缺少必填参数result_code！");
            }
            if (!inputObj.IsSet("user_ip"))
            {
                throw new WxPayException("访问接口IP，缺少必填参数user_ip！");
            }
            if (!inputObj.IsSet("execute_time_"))
            {
                throw new WxPayException("接口耗时，缺少必填参数execute_time_！");
            }

            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
            inputObj.SetValue("user_ip", WxPayConfig.IP);//终端ip
            inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商户上报时间	 
            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
            inputObj.SetValue("sign", inputObj.MakeSign());//签名
            string xml = inputObj.ToXml();

            Log.Info("WxPayApi", "Report request : " + xml);

            string response = ""; //HttpService.Post(xml, url, false, timeOut);

            Log.Info("WxPayApi", "Report response : " + response);

            WxPayData result = new WxPayData();
            result.FromXml(response);
            return result;
        }


        /**
        * 生成随机串，随机串包含字母或数字
        * @return 随机串
        */
        public static string GenerateNonceStr()
        {
            return Guid.NewGuid().ToString().Replace("-", "");
        }


        /**
        * 生成随机串，随机串包含字母或数字
        * @return 随机串
        */
        private static string GetRandomDom(int count)
        {
            string t62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

            long ticks = DateTime.Now.Ticks;

            string gen = "";
            int ind = 0;
            while (ind < count)
            {
                byte low = (byte)((ticks >> ind * 6) & 61);
                gen += t62[low];
                ind++;
            }
            return gen;
        }

        /**
        * 生成时间戳，标准北京时间，时区为东八区，自1970年1月1日 0点0分0秒以来的秒数
        * 将c# DateTime时间格式转换为Unix时间戳格式  
        * @return 时间戳
        */
        private static long ConvertDateTimeToInt(System.DateTime time)
        {
            //System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1, 0, 0, 0, 0));
            //long t = (time.Ticks - startTime.Ticks) / 10000;   //除10000调整为13位      
            //return t;

            DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            long timeStamp = (long)(DateTime.Now - startTime).TotalSeconds; //时间戳
            return timeStamp;
        }

        /**
        * RSA加密
        * @return 加密后字符串
        */
        private static string RSAEncrypt(string content)
        {
            string privateKey = $"{WxPayConfig._Privatekey}";
            byte[] keyData = Convert.FromBase64String(privateKey);
            var rsa = RSA.Create();
            rsa.ImportPkcs8PrivateKey(keyData, out _);
            byte[] data = System.Text.Encoding.UTF8.GetBytes(content);
            return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
        }
    }
}